package com.maigao.api.interceptors.identify;

import com.maigao.core.exception.ServerCode;
import com.maigao.core.exception.ServerException;
import com.maigao.entity.TokenInfo;
import com.maigao.service.IdentifyService;
import com.maigao.util.context.Context;
import com.maigao.util.context.user.User;
import com.google.common.base.Joiner;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.Cookie;
import java.net.URI;
import java.util.HashMap;
import java.util.Set;
import java.util.function.Supplier;

@Slf4j
public class Validator {


    //Cookie校验referer host
    private static final String[] REFERER_ALLOW_HOSTS = {
            "api.jingshu.site"
    };

    private static final Set<String> REFERER_ABSOLUTE_HOSTS = Sets.newHashSet(
            "servicewechat.com",
            "appservice.qq.com",
            "smartapp.baidu.com",
            "smartapps.cn");

    private static final Joiner.MapJoiner cookieJoiner = Joiner.on("; ").withKeyValueSeparator("=");

    @Autowired
    private IdentifyService identifyService;

    public boolean validate(Identify identify, String accessKey,
                            Cookie[] cookies, String referer, String csrfValue) throws ServerException {
        if (identify == null) {
            return true;
        }

        Context context = Context.current();
        User user = new User(0, null, null, null, null, null);
        context.setUser(user);

        user.setAccessKey(accessKey);

        boolean isLogin = false;
        long mid = 0L;
        String uid = null;
        String userName = null;
        //有accessKey
        if (!StringUtils.isEmpty(accessKey)) {
            try {
                Identify.Retry retry = identify.retry();
                TokenInfo tokenInfo = retry(retry.retryAttempt(), retry.interval(), () -> identifyService.getTokenInfo(accessKey));
                if (tokenInfo == null) {
                    throw new ServerException(ServerCode.NOT_LOGIN.getCode(), "未登录");
                }
                isLogin = tokenInfo.isLogin();
                mid = tokenInfo.getMid();
                uid = tokenInfo.getUid();
                userName = tokenInfo.getUserName();
            } catch (Exception e) {
                log.warn("GetTokenInfo accessKey:{}", accessKey, e);
                //不允许匿名访问，且返回了业务错误，透传错误码
                if (!identify.anonymous()) {
                    throw e;
                }
            }
            //需要登录访问的接口未登录
            if (!identify.anonymous() && !isLogin) {
                throw new ServerException(ServerCode.NOT_LOGIN.getCode(), "未登录");
            }
        } else {
            String sessionData = "";
            String cookiesValue = "";
            //传了cookie
            if (cookies != null) {
                HashMap<String, String> cookieMap = new HashMap<>(cookies.length);
                for (Cookie cookie : cookies) {
                    cookieMap.put(cookie.getName(), cookie.getValue());
                    if ("SESSDATA".equals(cookie.getName())) {
                        sessionData = cookie.getValue();
                    }
                }
                cookiesValue = cookieJoiner.join(cookieMap);
                user.setCookies(cookiesValue);
            }

            //没有cookie也没有accessKey
            if (StringUtils.isEmpty(sessionData)) {
                //不允许匿名访问
                if (!identify.anonymous()) {
                    throw new ServerException(ServerCode.NOT_LOGIN.getCode(), "不允许匿名登录");
                } else {
                    return true;
                }
            }

            boolean validReferer = false;
            if (!StringUtils.isEmpty(referer)) {
                String refererHost;
                try {
                    refererHost = URI.create(referer).getHost();
                    for (String host : REFERER_ALLOW_HOSTS) {
                        if (refererHost != null && refererHost.endsWith(host)) {
                            validReferer = true;
                            break;
                        }
                    }
                    if (!validReferer && refererHost != null && REFERER_ABSOLUTE_HOSTS.contains(refererHost)) {
                        validReferer = true;
                    }

                } catch (IllegalArgumentException e) {
                    log.warn("Illegal http refer url, url:{}", referer, e);
                    validReferer = false;
                }
            }
            //invalid referer
            if (!validReferer) {
                //允许匿名 直接返回 不验证
                if (identify.anonymous()) {
                    return true;
                }
                log.warn("Referer invalid referer:{}", referer);
                throw new ServerException(ServerCode.FORBIDDEN);
            }
        }

        //set mid
        user.setMid(mid);
        user.setUid(uid);
        user.setUserName(userName);
        return true;
    }

    private <T> T retry(int retryAttempt, int interval, Supplier<T> function) {
        for (int retryCount = 0; ; retryCount++) {
            try {
                return function.get();
            } catch (Exception ex) {
                if (retryCount < retryAttempt) {
                    try {
                        Thread.sleep(interval);
                    } catch (InterruptedException e) {
                        log.warn("current thread sleep error", e);
                        throw ex;
                    }
                    log.warn("rpc request timeout,active retry!retryCount={}", retryCount + 1);
                    continue;
                }
                throw ex;
            }
        }
    }
}